package com.seele.tigerwallet.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.seele.tigerwallet.async.Log;
import com.seele.tigerwallet.model.TokenInfoModel;
import com.seele.tigerwallet.model.UserInfo;
import com.seele.tigerwallet.ui.manager.DaoManager;
import com.seele.tigerwallet.wallet.EthWallet;

import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.crypto.MnemonicException;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.ObjectMapperFactory;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;

public class EthWalletUtil {


    public static final String TAG = EthWalletUtil.class.getSimpleName();

    public static SecureRandom secureRandom = new SecureRandom();
    private static final String ETH_TYPE = "m/44'/60'/0'/0/0";

    private static final String PATH = "M/44H/60H/0H/0/0";

    /**
     * 创建助记词语
     *
     * @return
     */
    public static List<String> getMnemonicCode() {
        Log.e("EthWalletUtil", "::" + System.currentTimeMillis());
        String password = "";
        long creationTimeSeconds = System.currentTimeMillis() / 1000;
        NetworkParameters params = TestNet3Params.get();
        DeterministicSeed seed = new DeterministicSeed(secureRandom, 128, password, creationTimeSeconds);
        List<String> mnemonic = seed.getMnemonicCode();
        return mnemonic;
    }


    /**
     * 创建钱包
     *
     * @param mnemonicCode
     * @param password
     * @return
     */
    public static TokenInfoModel createEthWallet(List<String> mnemonicCode, String password) {
        TokenInfoModel tokenInfoModel = null;
        if (null != mnemonicCode && mnemonicCode.size() > 0) {
            tokenInfoModel = new TokenInfoModel();
            String passphrase = "";
            long creationTimeSeconds = System.currentTimeMillis() / 1000;
            DeterministicSeed ds = new DeterministicSeed(mnemonicCode, null, passphrase, creationTimeSeconds);
            byte[] seedBytes = ds.getSeedBytes();

            if (seedBytes == null) {
                Log.e(TAG, "seedBytes is null");
                return null;
            }
            String[] pathArray = ETH_TYPE.split("/");
            DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);
            for (int i = 1; i < pathArray.length; i++) {
                ChildNumber childNumber;
                if (pathArray[i].endsWith("'")) {
                    int number = Integer.parseInt(pathArray[i].substring(0,
                            pathArray[i].length() - 1));
                    childNumber = new ChildNumber(number, true);
                } else {
                    int number = Integer.parseInt(pathArray[i]);
                    childNumber = new ChildNumber(number, false);
                }
                dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber);
            }
            ECKeyPair keyPair = ECKeyPair.create(dkKey.getPrivKeyBytes());

            try {
                WalletFile walletFile = Wallet.createLight(password, keyPair);
                tokenInfoModel.address = "0x" + walletFile.getAddress();
                ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
                String jsonStr = objectMapper.writeValueAsString(walletFile);
                String pwd = password + Utils.getSignature().substring(0, 18);
                jsonStr = CryptoUtils.encrypt(jsonStr, pwd);
                tokenInfoModel.keystore = jsonStr;
            } catch (Exception e) {
                Log.i(TAG, "createEthWallet error:" + e.getMessage());
                return null;
            }
            String name = "EthWallet";
            tokenInfoModel.name = name;
        }
        return tokenInfoModel;
    }


    public static EthWallet importEthWallet(List<String> seedCode) {
        EthWallet ethWallet = null;
        try {
            ethWallet = new EthWallet();
            //ethWallet.mnemonic = seedCode;
            // BitcoinJ
            long creationTimeSeconds = System.currentTimeMillis() / 1000;
            String password = "";
            DeterministicSeed seed = new DeterministicSeed(secureRandom, 128, password, creationTimeSeconds);
            DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
            List<ChildNumber> keyPath = HDUtils.parsePath(ETH_TYPE);
            DeterministicKey key = chain.getKeyByPath(keyPath, true);
            BigInteger privKey = key.getPrivKey();

            // Web3j
            Credentials credentials = Credentials.create(privKey.toString(16));
            String address = credentials.getAddress();
            String privateKey = privKey.toString(16);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ethWallet;


    }


    public static void importEthWallet3(String value) {

//        strings2.add("holiday");
//        strings2.add("coral");
//        strings2.add("nose");
//        strings2.add("orphan");
//        strings2.add("brush");
//        strings2.add("jump");
//        strings2.add("pass");
//        strings2.add("shed");
//        strings2.add("crunch");
//        strings2.add("private");
//        strings2.add("goddess");
//        strings2.add("lumber");

        Credentials credentials = null;

        credentials = WalletUtils.loadBip39Credentials("752532", "holiday coral nose orphan brush jump pass shed crunch private goddess lumber");

        // 钱包地址
        System.out.println(credentials.getAddress());

        Log.e(TAG, "redentials.getAddress()" + credentials.getAddress());
        // 公钥16进制字符串表示
        System.out.println(credentials.getEcKeyPair().getPublicKey().toString(16));

        Log.e(TAG, "getPublicKey" + credentials.getEcKeyPair().getPublicKey().toString(16));
        // 私钥16进制字符串表示
        System.out.println(credentials.getEcKeyPair().getPrivateKey().toString(16));

        Log.e(TAG, "getPrivateKey" + credentials.getEcKeyPair().getPrivateKey().toString(16));
    }

    public static EthWallet importEthWallet2(List<String> mnemonic) {
        EthWallet ethWallet = new EthWallet();
        //ethWallet.mnemonic = mnemonic;
        String passphrase = "";
        long creationTimeSeconds = System.currentTimeMillis() / 1000;
        DeterministicSeed ds = new DeterministicSeed(mnemonic, null, passphrase, creationTimeSeconds);

        //根私钥
        byte[] seedBytes = ds.getSeedBytes();
        android.util.Log.i(TAG, "根私钥 " + Arrays.toString(seedBytes));
        //助记词
        android.util.Log.i(TAG, "助记词 " + Arrays.toString(mnemonic.toArray()));

        if (seedBytes == null) {
            android.util.Log.e(TAG, "importEthWallet failed,seedBytes == null");
            return null;
        }
        String[] pathArray = ETH_TYPE.split("/");
        DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);
        for (int i = 1; i < pathArray.length; i++) {
            ChildNumber childNumber;
            if (pathArray[i].endsWith("'")) {
                int number = Integer.parseInt(pathArray[i].substring(0,
                        pathArray[i].length() - 1));
                childNumber = new ChildNumber(number, true);
            } else {
                int number = Integer.parseInt(pathArray[i]);
                childNumber = new ChildNumber(number, false);
            }
            dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber);
        }
        android.util.Log.i(TAG, "path " + dkKey.getPathAsString());

        ECKeyPair keyPair = ECKeyPair.create(dkKey.getPrivKeyBytes());
        android.util.Log.i(TAG, "eth publicKey " + keyPair.getPublicKey().toString(16) + ",length:" + keyPair.getPublicKey().toString(16).length());

        try {
            WalletFile walletFile = Wallet.createLight("752532", keyPair);
            ethWallet.address = "0x" + walletFile.getAddress();
            android.util.Log.i(TAG, "eth address " + "0x" + walletFile.getAddress() + ",length:" + walletFile.getAddress().length());

        } catch (CipherException e) {
            e.printStackTrace();
            android.util.Log.i(TAG, e.getMessage());
        }

        return ethWallet;
    }

    public static String getPriKey(String keystore, String password) {
        String priKey = "";
        try {
            ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
            WalletFile checkWalletFile = objectMapper.readValue(keystore, WalletFile.class);
            ECKeyPair ecKeyPair = Wallet.decrypt(password, checkWalletFile);
            ecKeyPair.getPrivateKey();
            priKey = "0x" + ecKeyPair.getPrivateKey().toString(16);
        } catch (Exception e) {
            android.util.Log.e(TAG, "getPriKey error:" + e.getMessage());
            priKey = "";
        }
        return priKey;
    }




    public static boolean checkPassowrd(String password) {
        String pubKey = "";
        try {
            ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
            List<TokenInfoModel> tokenInfoModels = DaoManager.getTokenDao().queryBuilder().where().eq("address", UserInfo.get().address).query();
            String keystore = "";
            if (tokenInfoModels != null && tokenInfoModels.size() >= 1) {
                keystore = tokenInfoModels.get(0).keystore;
                String pwd = password + Utils.getSignature().substring(0, 18);
                keystore = CryptoUtils.decrypt(keystore, pwd);

            }
            WalletFile checkWalletFile = objectMapper.readValue(keystore, WalletFile.class);
            ECKeyPair ecKeyPair = Wallet.decrypt(password, checkWalletFile);
            BigInteger pubKeyInteger = ecKeyPair.getPublicKey(); //如果能够解析出
            pubKey = "0x" + ecKeyPair.getPublicKey().toString(16);
            android.util.Log.i(TAG, "pubKey:" + pubKey);
        } catch (Exception e) {
            android.util.Log.e(TAG, "checkPassowrd error:" + e.getMessage());
            return false;
        }
        return true;
    }


    public static boolean checkMnemonic(List<String> list) {
        String passphrase = "";
        String password = "";
        long creationTimeSeconds = System.currentTimeMillis() / 1000;
        DeterministicSeed ds = new DeterministicSeed(list, null, passphrase, creationTimeSeconds);
        //根私钥
        byte[] seedBytes = ds.getSeedBytes();
        //助记词
        List<String> mnemonic = ds.getMnemonicCode();
        try {
            //助记词种子
            byte[] mnemonicSeedBytes = MnemonicCode.INSTANCE.toEntropy(mnemonic);
            ECKeyPair mnemonicKeyPair = ECKeyPair.create(mnemonicSeedBytes);
            WalletFile walletFile = Wallet.createLight(password, mnemonicKeyPair);
            ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
            //存这个keystore 用完后删除
            String jsonStr = objectMapper.writeValueAsString(walletFile);
            android.util.Log.i(TAG, "keystore:" + jsonStr);
            //验证
            WalletFile checkWalletFile = objectMapper.readValue(jsonStr, WalletFile.class);
            ECKeyPair ecKeyPair = Wallet.decrypt(password, checkWalletFile);
            byte[] checkMnemonicSeedBytes = Numeric.hexStringToByteArray(ecKeyPair.getPrivateKey().toString(16));
            List<String> checkMnemonic = MnemonicCode.INSTANCE.toMnemonic(checkMnemonicSeedBytes);
        } catch (MnemonicException.MnemonicLengthException | MnemonicException.MnemonicWordException | MnemonicException.MnemonicChecksumException | CipherException | IOException e) {
            android.util.Log.e(TAG, "checkMnemonic error:" + e.getMessage());
            return false;
        }
        return true;
    }


}
